home *** CD-ROM | disk | FTP | other *** search
/ Light ROM 1 / LIGHT-ROM 1 (Amiga Library Services)(1994).iso / ffdisks / d982.lha / HWGRCS / HWGdiffP1_2_6.lha / diffutils-2.6 / src / sdiff.c < prev    next >
C/C++ Source or Header  |  1993-12-23  |  30KB  |  1,326 lines

  1. /* SDIFF -- interactive merge front end to diff
  2.    Copyright (C) 1992, 1993 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU SDIFF was written by Thomas Lord. */
  21.  
  22. #include "system.h"
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <signal.h>
  26. #include "getopt.h"
  27.  
  28. /* Size of chunks read from files which must be parsed into lines. */
  29. #define SDIFF_BUFSIZE ((size_t) 65536)
  30.  
  31. /* Default name of the diff program */
  32. #ifndef DIFF_PROGRAM
  33. #define DIFF_PROGRAM "/usr/bin/diff"
  34. #endif
  35.  
  36. /* Users' editor of nonchoice */
  37. #ifndef DEFAULT_EDITOR
  38. #define DEFAULT_EDITOR "ed"
  39. #endif
  40.  
  41. extern char version_string[];
  42. static char const *prog;
  43. static char const *diffbin = DIFF_PROGRAM;
  44. static char const *edbin = DEFAULT_EDITOR;
  45.  
  46. #ifdef _AMIGA
  47. static char const **diffargv;
  48. #endif
  49.  
  50. static char *tmpname;
  51. static int volatile tmpmade;
  52. static pid_t volatile diffpid;
  53.  
  54. struct line_filter;
  55.  
  56. static FILE *ck_fdopen PARAMS((int, char const *));
  57. static FILE *ck_fopen PARAMS((char const *, char const *));
  58. static RETSIGTYPE catchsig PARAMS((int));
  59. static VOID *xmalloc PARAMS((size_t));
  60. static char const *expand_name PARAMS((char *, int, char const *));
  61. static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
  62. static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
  63. static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
  64. static int skip_white PARAMS((void));
  65. static size_t ck_fread PARAMS((char *, size_t, FILE *));
  66. static size_t lf_refill PARAMS((struct line_filter *));
  67. static void checksigs PARAMS((void));
  68. static void ck_fclose PARAMS((FILE *));
  69. static void ck_fflush PARAMS((FILE *));
  70. static void ck_fwrite PARAMS((char const *, size_t, FILE *));
  71. static void cleanup PARAMS((void));
  72. static void diffarg PARAMS((char const *));
  73. static void execdiff PARAMS((int, char const *, char const *, char const *));
  74. static void exiterr PARAMS((void));
  75. static void fatal PARAMS((char const *));
  76. static void flush_line PARAMS((void));
  77. static void give_help PARAMS((void));
  78. static void lf_copy PARAMS((struct line_filter *, int, FILE *));
  79. static void lf_init PARAMS((struct line_filter *, FILE *));
  80. static void lf_skip PARAMS((struct line_filter *, int));
  81. static void perror_fatal PARAMS((char const *));
  82. static void trapsigs PARAMS((void));
  83. static void untrapsig PARAMS((int));
  84. static void usage PARAMS((int));
  85.  
  86. /* this lossage until the gnu libc conquers the universe */
  87. #ifdef _AMIGA
  88. #define PVT_tmpdir "T:"
  89. #else
  90. #define PVT_tmpdir "/tmp"
  91. #endif
  92. static char *private_tempnam PARAMS((char const *, char const *, int, size_t *));
  93. static int diraccess PARAMS((char const *));
  94. static int exists PARAMS((char const *));
  95.  
  96. /* Options: */
  97.  
  98. /* name of output file if -o spec'd */
  99. static char *out_file;
  100.  
  101. /* do not print common lines if true, set by -s option */
  102. static int suppress_common_flag;
  103.  
  104. static struct option const longopts[] =
  105. {
  106.   {"ignore-blank-lines", 0, 0, 'B'},
  107.   {"speed-large-files", 0, 0, 'H'},
  108.   {"ignore-matching-lines", 1, 0, 'I'},
  109.   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
  110.   {"text", 0, 0, 'a'},
  111.   {"ignore-space-change", 0, 0, 'b'},
  112.   {"minimal", 0, 0, 'd'},
  113.   {"ignore-case", 0, 0, 'i'},
  114.   {"left-column", 0, 0, 'l'},
  115.   {"output", 1, 0, 'o'},
  116.   {"suppress-common-lines", 0, 0, 's'},
  117.   {"expand-tabs", 0, 0, 't'},
  118.   {"width", 1, 0, 'w'},
  119.   {"version", 0, 0, 'v'},
  120.   {"help", 0, 0, 129},
  121.   {0, 0, 0, 0}
  122. };
  123.  
  124. /* prints usage message and quits */
  125. static void
  126. usage (status)
  127.      int status;
  128. {
  129.   printf ("Usage: %s [options] from-file to-file\n", prog);
  130.   printf ("Options:\n\
  131.         [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
  132.         [--expand-tabs] [--help] [--ignore-all-space] [--ignore-blank-lines]\n\
  133.         [--ignore-case] [--ignore-matching-lines=regexp]\n\
  134.         [--ignore-space-change] [--left-column] [--minimal]\n\
  135.         [--output=outfile] [--speed-large-files] [--suppress-common-lines]\n\
  136.         [--text] [--version] [--width=columns]\n");
  137.   exit (status);
  138. }
  139.  
  140. static void
  141. cleanup ()
  142. {
  143. #ifndef _AMIGA
  144.   if (0 < diffpid)
  145.     kill (diffpid, SIGPIPE);
  146. #endif
  147.   if (tmpmade)
  148.     unlink (tmpname);
  149. }
  150.  
  151. static void
  152. exiterr ()
  153. {
  154.   cleanup ();
  155.   untrapsig (0);
  156.   checksigs ();
  157.   exit (2);
  158. }
  159.  
  160. static void
  161. fatal (msg)
  162.      char const *msg;
  163. {
  164.   fprintf (stderr, "%s: %s\n", prog, msg);
  165.   exiterr ();
  166. }
  167.  
  168. static void
  169. perror_fatal (msg)
  170.      char const *msg;
  171. {
  172.   int e = errno;
  173.   checksigs ();
  174.   fprintf (stderr, "%s: ", prog);
  175.   errno = e;
  176.   perror (msg);
  177.   exiterr ();
  178. }
  179.  
  180.  
  181. /* malloc freely or DIE! */
  182. static VOID *
  183. xmalloc (size)
  184.      size_t size;
  185. {
  186.   VOID *r = (VOID *) malloc (size);
  187.   if (!r)
  188.     fatal ("memory exhausted");
  189.   return r;
  190. }
  191.  
  192. static FILE *
  193. ck_fopen (fname, type)
  194.      char const *fname, *type;
  195. {
  196.   FILE *r = fopen (fname, type);
  197.   if (!r)
  198.     perror_fatal (fname);
  199.   return r;
  200. }
  201.  
  202.  
  203. static FILE *
  204. ck_fdopen (fd, type)
  205.      int fd;
  206.      char const *type;
  207. {
  208.   FILE *r = fdopen (fd, type);
  209.   if (!r)
  210.     perror_fatal ("fdopen");
  211.   return r;
  212. }
  213.  
  214. static void
  215. ck_fclose (f)
  216.      FILE *f;
  217. {
  218.   if (fclose (f))
  219.     perror_fatal ("input/output error");
  220. }
  221.  
  222. static size_t
  223. ck_fread (buf, size, f)
  224.      char *buf;
  225.      size_t size;
  226.      FILE *f;
  227. {
  228.   size_t r = fread (buf, sizeof (char), size, f);
  229.   if (r == 0 && ferror (f))
  230.     perror_fatal ("input error");
  231.   return r;
  232. }
  233.  
  234. static void
  235. ck_fwrite (buf, size, f)
  236.      char const *buf;
  237.      size_t size;
  238.      FILE *f;
  239. {
  240.   if (fwrite (buf, sizeof (char), size, f) != size)
  241.     perror_fatal ("output error");
  242. }
  243.  
  244. static void
  245. ck_fflush (f)
  246.      FILE *f;
  247. {
  248.   if (fflush (f) != 0)
  249.     perror_fatal ("output error");
  250. }
  251.  
  252. #if !HAVE_MEMCHR
  253. char *
  254. memchr (s, c, n)
  255.      char const *s;
  256.      int c;
  257.      size_t n;
  258. {
  259.   unsigned char const *p = (unsigned char const *) s, *lim = p + n;
  260.   for (;  p < lim;  p++)
  261.     if (*p == c)
  262.       return (char *) p;
  263.   return 0;
  264. }
  265. #endif
  266.  
  267. #ifndef HAVE_WAITPID
  268. /* Emulate waitpid well enough for sdiff, which has at most two children.  */
  269. static pid_t
  270. waitpid (pid, stat_loc, options)
  271.      pid_t pid;
  272.      int *stat_loc;
  273.      int options;
  274. {
  275.   static int ostatus;
  276.   static pid_t opid;
  277.   int npid, status;
  278.  
  279.   if (pid == opid)
  280.     {
  281.       opid = 0;
  282.       status = ostatus;
  283.     }
  284.   else
  285.     while ((npid = wait (&status)) != pid)
  286.       {
  287.         if (npid < 0)
  288.           return npid;
  289.         opid = npid;
  290.         ostatus = status;
  291.       }
  292.   *stat_loc = status;
  293.   return pid;
  294. }
  295. #endif
  296.  
  297. static char const *
  298. expand_name (name, isdir, other_name)
  299.      char *name;
  300.      int isdir;
  301.      char const *other_name;
  302. {
  303.   if (strcmp (name, "-") == 0)
  304.     fatal ("cannot interactively merge standard input");
  305.   if (!isdir)
  306.     return name;
  307.   else
  308.     {
  309.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  310. #ifdef _AMIGA
  311.       char const
  312.         *p = strrchr (other_name, '/');
  313.       char *base, *r;
  314.       size_t namelen, baselen;
  315.  
  316.       if(!p)
  317.       {
  318.           p = strrchr(other_name, ':');
  319.       } /* if */
  320.  
  321.       base = p ? p+1 : other_name;
  322.  
  323.       namelen = strlen (name);
  324.       baselen = strlen (base);
  325.       r = xmalloc (namelen + baselen + 2);
  326.  
  327.       memcpy (r, name, namelen);
  328.       if(namelen && r[namelen - 1] != ':' && r[namelen - 1] != '/')
  329.       {
  330.           r[namelen++] = '/';
  331.       } /* if */
  332.       memcpy (r + namelen, base, baselen + 1);
  333. #else
  334.       char const
  335.         *p = strrchr (other_name, '/'),
  336.         *base = p ? p+1 : other_name;
  337.       size_t namelen = strlen (name), baselen = strlen (base);
  338.       char *r = xmalloc (namelen + baselen + 2);
  339.       memcpy (r, name, namelen);
  340.       r[namelen] = '/';
  341.       memcpy (r + namelen + 1, base, baselen + 1);
  342. #endif
  343.       return r;
  344.     }
  345. }
  346.  
  347.  
  348.  
  349. struct line_filter {
  350.   FILE *infile;
  351.   char *bufpos;
  352.   char *buffer;
  353.   char *buflim;
  354. };
  355.  
  356. static void
  357. lf_init (lf, infile)
  358.      struct line_filter *lf;
  359.      FILE *infile;
  360. {
  361.   lf->infile = infile;
  362.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  363.   lf->buflim[0] = '\n';
  364. }
  365.  
  366. /* Fill an exhausted line_filter buffer from its INFILE */
  367. static size_t
  368. lf_refill (lf)
  369.      struct line_filter *lf;
  370. {
  371.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  372.   lf->bufpos = lf->buffer;
  373.   lf->buflim = lf->buffer + s;
  374.   lf->buflim[0] = '\n';
  375.   checksigs ();
  376.   return s;
  377. }
  378.  
  379. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  380. static void
  381. lf_copy (lf, lines, outfile)
  382.      struct line_filter *lf;
  383.      int lines;
  384.      FILE *outfile;
  385. {
  386.   char *start = lf->bufpos;
  387.  
  388.   while (lines)
  389.     {
  390.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  391.       if (! lf->bufpos)
  392.         {
  393.           ck_fwrite (start, lf->buflim - start, outfile);
  394.           if (! lf_refill (lf))
  395.             return;
  396.           start = lf->bufpos;
  397.         }
  398.       else
  399.         {
  400.           --lines;
  401.           ++lf->bufpos;
  402.         }
  403.     }
  404.  
  405.   ck_fwrite (start, lf->bufpos - start, outfile);
  406. }
  407.  
  408. /* Advance LINES on LF's infile without doing output */
  409. static void
  410. lf_skip (lf, lines)
  411.      struct line_filter *lf;
  412.      int lines;
  413. {
  414.   while (lines)
  415.     {
  416.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  417.       if (! lf->bufpos)
  418.         {
  419.           if (! lf_refill (lf))
  420.             break;
  421.         }
  422.       else
  423.         {
  424.           --lines;
  425.           ++lf->bufpos;
  426.         }
  427.     }
  428. }
  429.  
  430. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  431. static int
  432. lf_snarf (lf, buffer, bufsize)
  433.      struct line_filter *lf;
  434.      char *buffer;
  435.      size_t bufsize;
  436. {
  437.   char *start = lf->bufpos;
  438.  
  439.   for (;;)
  440.     {
  441.       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
  442.       size_t s = next - start;
  443.       if (bufsize <= s)
  444.         return 0;
  445.       memcpy (buffer, start, s);
  446.       if (next < lf->buflim)
  447.         {
  448.           buffer[s] = 0;
  449.           lf->bufpos = next + 1;
  450.           return 1;
  451.         }
  452.       if (! lf_refill (lf))
  453.         return s ? 0 : EOF;
  454.       buffer += s;
  455.       bufsize -= s;
  456.       start = next;
  457.     }
  458. }
  459.  
  460.  
  461.  
  462. int
  463. main (argc, argv)
  464.      int argc;
  465.      char *argv[];
  466. {
  467.   int opt;
  468.   char *editor = getenv ("EDITOR");
  469.   char *differ = getenv ("DIFF");
  470.  
  471.   prog = argv[0];
  472.   if (editor)
  473.     edbin = editor;
  474.   if (differ)
  475.     diffbin = differ;
  476.  
  477.   diffarg ("diff");
  478.  
  479.   /* parse command line args */
  480.   while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
  481.          != EOF)
  482.     {
  483.       switch (opt)
  484.         {
  485.         case 'a':
  486.           diffarg ("-a");
  487.           break;
  488.  
  489.         case 'b':
  490.           diffarg ("-b");
  491.           break;
  492.  
  493.         case 'B':
  494.           diffarg ("-B");
  495.           break;
  496.  
  497.         case 'd':
  498.           diffarg ("-d");
  499.           break;
  500.  
  501.         case 'H':
  502.           diffarg ("-H");
  503.           break;
  504.  
  505.         case 'i':
  506.           diffarg ("-i");
  507.           break;
  508.  
  509.         case 'I':
  510.           diffarg ("-I");
  511.           diffarg (optarg);
  512.           break;
  513.  
  514.         case 'l':
  515.           diffarg ("--left-column");
  516.           break;
  517.  
  518.         case 'o':
  519.           out_file = optarg;
  520.           break;
  521.  
  522.         case 's':
  523.           suppress_common_flag = 1;
  524.           break;
  525.  
  526.         case 't':
  527.           diffarg ("-t");
  528.           break;
  529.  
  530.         case 'v':
  531.           printf ("GNU sdiff version %s\n", version_string);
  532.           exit (0);
  533.  
  534.         case 'w':
  535.           diffarg ("-W");
  536.           diffarg (optarg);
  537.           break;
  538.  
  539.         case 'W':
  540.           diffarg ("-w");
  541.           break;
  542.  
  543.         case 129:
  544.           usage (0);
  545.  
  546.         default:
  547.           usage (2);
  548.         }
  549.     }
  550.  
  551.   if (argc - optind != 2)
  552.     usage (2);
  553.  
  554.   if (! out_file)
  555.     /* easy case: diff does everything for us */
  556.     execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]);
  557.   else
  558.     {
  559.       FILE *left, *right, *out, *diffout;
  560.       int diff_fds[2];
  561.       int interact_ok;
  562.       pid_t pid;
  563.       struct line_filter lfilt;
  564.       struct line_filter rfilt;
  565.       struct line_filter diff_filt;
  566.       int leftdir = diraccess (argv[optind]);
  567.       int rightdir = diraccess (argv[optind + 1]);
  568.  
  569.       if (leftdir && rightdir)
  570.         fatal ("both files to be compared are directories");
  571.  
  572.       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
  573.       ;
  574.       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
  575.       out = ck_fopen (out_file, "w");
  576.  
  577. #ifdef _AMIGA
  578.     {
  579.       int wstatus;
  580.       FILE *popenv(const char **, const char *);
  581.  
  582.       trapsigs();
  583.  
  584.       diffarg("--sdiff-merge-assist");
  585.       diffarg("--");
  586.       diffarg(argv[optind]);
  587.       diffarg(argv[optind + 1]);
  588.       diffarg (0);
  589.  
  590.       /* Well, the next line is a hack, but a reasonable one in this context */
  591.       diffargv[0] = diffbin;
  592.       diffout = popenv(diffargv, "r");
  593.       if(!diffout)
  594.       {
  595.         perror_fatal ("input/output error");
  596.       } /* if */
  597.  
  598.       lf_init (&diff_filt, diffout);
  599.       lf_init (&lfilt, left);
  600.       lf_init (&rfilt, right);
  601.  
  602.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  603.  
  604.       wstatus =  pclose (diffout);
  605.       ck_fclose (left);
  606.       ck_fclose (right);
  607.       ck_fclose (out);
  608.  
  609.         if (tmpmade)
  610.           {
  611.             unlink (tmpname);
  612.             tmpmade = 0;
  613.           }
  614.  
  615.         if (! interact_ok)
  616.           exiterr ();
  617.  
  618.         untrapsig (0);
  619.         checksigs ();
  620.         exit (WEXITSTATUS (wstatus));
  621.     }
  622. #else
  623.       if (pipe (diff_fds))
  624.         perror_fatal ("pipe");
  625.  
  626.       trapsigs ();
  627.  
  628.       diffpid = pid = vfork ();
  629.  
  630.       if (pid == 0)
  631.         {
  632.           signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
  633.           signal (SIGPIPE, SIG_DFL);
  634.  
  635.           close (diff_fds[0]);
  636.           if (diff_fds[1] != STDOUT_FILENO)
  637.             {
  638.               dup2 (diff_fds[1], STDOUT_FILENO);
  639.               close (diff_fds[1]);
  640.             }
  641.  
  642.           execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]);
  643.         }
  644.  
  645.       if (pid < 0)
  646.         perror_fatal ("fork failed");
  647.  
  648.       close (diff_fds[1]);
  649.       diffout = ck_fdopen (diff_fds[0], "r");
  650.  
  651.       lf_init (&diff_filt, diffout);
  652.       lf_init (&lfilt, left);
  653.       lf_init (&rfilt, right);
  654.  
  655.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  656.  
  657.       ck_fclose (diffout);
  658.       ck_fclose (left);
  659.       ck_fclose (right);
  660.       ck_fclose (out);
  661.  
  662.       {
  663.         int wstatus;
  664.  
  665.         while (waitpid (pid, &wstatus, 0) < 0)
  666.           if (errno == EINTR)
  667.             checksigs ();
  668.           else
  669.             perror_fatal ("wait failed");
  670.         diffpid = 0;
  671.  
  672.         if (tmpmade)
  673.           {
  674.             unlink (tmpname);
  675.             tmpmade = 0;
  676.           }
  677.  
  678.         if (! interact_ok)
  679.           exiterr ();
  680.  
  681.         if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
  682.           fatal ("Subsidiary diff failed");
  683.  
  684.         untrapsig (0);
  685.         checksigs ();
  686.         exit (WEXITSTATUS (wstatus));
  687.       }
  688. #endif
  689.     }
  690.   return 0;                     /* Fool -Wall . . . */
  691. }
  692.  
  693. #ifndef _AMIGA
  694. static char const **diffargv;
  695. #endif
  696.  
  697. static void
  698. diffarg (a)
  699.      char const *a;
  700. {
  701.   static unsigned diffargs, diffargsmax;
  702.  
  703.   if (diffargs == diffargsmax)
  704.     {
  705.       if (! diffargsmax)
  706.         {
  707.           diffargv = (char const **) xmalloc (sizeof (char));
  708.           diffargsmax = 8;
  709.         }
  710.       diffargsmax *= 2;
  711.       diffargv = (char const **) realloc (diffargv,
  712.                                           diffargsmax * sizeof (char const *));
  713.       if (! diffargv)
  714.         fatal ("out of memory");
  715.     }
  716.   diffargv[diffargs++] = a;
  717. }
  718.  
  719. static void
  720. execdiff (differences_only, option, file1, file2)
  721.      int differences_only;
  722.      char const *option, *file1, *file2;
  723. {
  724.   if (differences_only)
  725.     diffarg ("--suppress-common-lines");
  726.   diffarg (option);
  727.   diffarg ("--");
  728.   diffarg (file1);
  729.   diffarg (file2);
  730.   diffarg (0);
  731.  
  732. #ifdef _AMIGA
  733.     {
  734.         int result;
  735.  
  736.         result = AMIGA_run_v(NULL, NULL, NULL, 0, NULL, 0, diffargv, diffbin);
  737.  
  738.         if(!result)
  739.         {
  740.             exit(0);
  741.         } /* if */
  742.  
  743.         fprintf (stderr, "%s execution caused an error\n", diffbin);
  744.         exit (2);
  745.     }
  746. #else
  747.   execvp (diffbin, (char **) diffargv);
  748.   write (STDERR_FILENO, diffbin, strlen (diffbin));
  749.   write (STDERR_FILENO, ": not found\n", 12);
  750.   _exit (2);
  751. #endif
  752. }
  753.  
  754.  
  755.  
  756.  
  757. /* Signal handling */
  758.  
  759. #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
  760. static int const sigs[] = {
  761. #ifdef SIGHUP
  762.        SIGHUP,
  763. #endif
  764. #ifdef SIGQUIT
  765.        SIGQUIT,
  766. #endif
  767. #ifdef SIGTERM
  768.        SIGTERM,
  769. #endif
  770. #ifdef SIGXCPU
  771.        SIGXCPU,
  772. #endif
  773. #ifdef SIGXFSZ
  774.        SIGXFSZ,
  775. #endif
  776. #ifdef _AMIGA
  777.        SIGINT
  778. #else
  779.        SIGINT,
  780.        SIGPIPE
  781. #endif
  782. };
  783.  
  784. /* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
  785. #if HAVE_SIGACTION
  786. static struct sigaction initial_action[NUM_SIGS];
  787. #define initial_handler(i) (initial_action[i].sa_handler)
  788. #else
  789. static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
  790. #define initial_handler(i) (initial_action[i])
  791. #endif
  792.  
  793. static int volatile ignore_SIGINT;
  794. static int volatile signal_received;
  795. static int sigs_trapped;
  796.  
  797. static RETSIGTYPE
  798. catchsig (s)
  799.      int s;
  800. {
  801. #if ! HAVE_SIGACTION
  802.   signal (s, SIG_IGN);
  803. #endif
  804.   if (! (s == SIGINT && ignore_SIGINT))
  805.     signal_received = s;
  806. }
  807.  
  808. static void
  809. trapsigs ()
  810. {
  811.   int i;
  812.  
  813. #if HAVE_SIGACTION
  814.   struct sigaction catchaction;
  815.   bzero (&catchaction, sizeof (catchaction));
  816.   catchaction.sa_handler = catchsig;
  817. #ifdef SA_INTERRUPT
  818.   /* Non-Posix BSD-style systems like SunOS 4.1.x need this
  819.      so that `read' calls are interrupted properly.  */
  820.   catchaction.sa_flags = SA_INTERRUPT;
  821. #endif
  822.   sigemptyset (&catchaction.sa_mask);
  823.   for (i = 0;  i < NUM_SIGS;  i++)
  824.     sigaddset (&catchaction.sa_mask, sigs[i]);
  825.   for (i = 0;  i < NUM_SIGS;  i++)
  826.     {
  827.       sigaction (sigs[i], 0, &initial_action[i]);
  828.       if (initial_handler (i) != SIG_IGN
  829.           && sigaction (sigs[i], &catchaction, 0) != 0)
  830.         fatal ("signal error");
  831.     }
  832. #else /* ! HAVE_SIGACTION */
  833.   for (i = 0;  i < NUM_SIGS;  i++)
  834.     {
  835.       initial_action[i] = signal (sigs[i], SIG_IGN);
  836.       if (initial_handler (i) != SIG_IGN
  837.           && signal (sigs[i], catchsig) != SIG_IGN)
  838.         fatal ("signal error");
  839.     }
  840. #endif /* ! HAVE_SIGACTION */
  841.   sigs_trapped = 1;
  842. }
  843.  
  844. /* Untrap signal S, or all trapped signals if S is zero.  */
  845. static void
  846. untrapsig (s)
  847.      int s;
  848. {
  849.   int i;
  850.  
  851.   if (sigs_trapped)
  852.     for (i = 0;  i < NUM_SIGS;  i++)
  853.       if ((!s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
  854. #if HAVE_SIGACTION
  855.           sigaction (sigs[i], &initial_action[i], 0);
  856. #else
  857.           signal (sigs[i], initial_action[i]);
  858. #endif
  859. }
  860.  
  861. /* Exit if a signal has been received.  */
  862. static void
  863. checksigs ()
  864. {
  865.   int s = signal_received;
  866.   if (s)
  867.     {
  868.       cleanup ();
  869.  
  870.       /* Yield an exit status indicating that a signal was received.  */
  871.       untrapsig (s);
  872. #ifndef _AMIGA
  873.       kill (getpid (), s);
  874. #endif
  875.  
  876.       /* That didn't work, so exit with error status.  */
  877.       exit (2);
  878.     }
  879. }
  880.  
  881.  
  882.  
  883. static void
  884. give_help ()
  885. {
  886.   fprintf (stderr,"l:\tuse the left version\n");
  887.   fprintf (stderr,"r:\tuse the right version\n");
  888.   fprintf (stderr,"e l:\tedit then use the left version\n");
  889.   fprintf (stderr,"e r:\tedit then use the right version\n");
  890.   fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
  891.   fprintf (stderr,"e:\tedit a new version\n");
  892.   fprintf (stderr,"s:\tsilently include common lines\n");
  893.   fprintf (stderr,"v:\tverbosely include common lines\n");
  894.   fprintf (stderr,"q:\tquit\n");
  895. }
  896.  
  897. static int
  898. skip_white ()
  899. {
  900.   int c;
  901.   while (isspace (c = getchar ()) && c != '\n')
  902.     checksigs ();
  903.   if (ferror (stdin))
  904.     perror_fatal ("input error");
  905.   return c;
  906. }
  907.  
  908. static void
  909. flush_line ()
  910. {
  911.   int c;
  912.   while ((c = getchar ()) != '\n' && c != EOF)
  913.     ;
  914.   if (ferror (stdin))
  915.     perror_fatal ("input error");
  916. }
  917.  
  918.  
  919. /* interpret an edit command */
  920. static int
  921. edit (left, lenl, right, lenr, outfile)
  922.      struct line_filter *left;
  923.      int lenl;
  924.      struct line_filter *right;
  925.      int lenr;
  926.      FILE *outfile;
  927. {
  928.   for (;;)
  929.     {
  930.       int cmd0, cmd1;
  931.       int gotcmd = 0;
  932.  
  933.       cmd1 = 0; /* Pacify `gcc -W'.  */
  934.  
  935.       while (!gotcmd)
  936.         {
  937.           if (putchar ('%') != '%')
  938.             perror_fatal ("output error");
  939.           ck_fflush (stdout);
  940.  
  941.           cmd0 = skip_white ();
  942.           switch (cmd0)
  943.             {
  944.             case 'l': case 'r': case 's': case 'v': case 'q':
  945.               if (skip_white () != '\n')
  946.                 {
  947.                   give_help ();
  948.                   flush_line ();
  949.                   continue;
  950.                 }
  951.               gotcmd = 1;
  952.               break;
  953.  
  954.             case 'e':
  955.               cmd1 = skip_white ();
  956.               switch (cmd1)
  957.                 {
  958.                 case 'l': case 'r': case 'b':
  959.                   if (skip_white () != '\n')
  960.                     {
  961.                       give_help ();
  962.                       flush_line ();
  963.                       continue;
  964.                     }
  965.                   gotcmd = 1;
  966.                   break;
  967.                 case '\n':
  968.                   gotcmd = 1;
  969.                   break;
  970.                 default:
  971.                   give_help ();
  972.                   flush_line ();
  973.                   continue;
  974.                 }
  975.               break;
  976.             case EOF:
  977.               if (feof (stdin))
  978.                 {
  979.                   gotcmd = 1;
  980.                   cmd0 = 'q';
  981.                   break;
  982.                 }
  983.               /* falls through */
  984.             default:
  985.               flush_line ();
  986.               /* falls through */
  987.             case '\n':
  988.               give_help ();
  989.               continue;
  990.             }
  991.         }
  992.  
  993.       switch (cmd0)
  994.         {
  995.         case 'l':
  996.           lf_copy (left, lenl, outfile);
  997.           lf_skip (right, lenr);
  998.           return 1;
  999.         case 'r':
  1000.           lf_copy (right, lenr, outfile);
  1001.           lf_skip (left, lenl);
  1002.           return 1;
  1003.         case 's':
  1004.           suppress_common_flag = 1;
  1005.           break;
  1006.         case 'v':
  1007.           suppress_common_flag = 0;
  1008.           break;
  1009.         case 'q':
  1010.           return 0;
  1011.         case 'e':
  1012.           if (! tmpname && ! (tmpname = private_tempnam (0, "sdiff", 1, 0)))
  1013.             perror_fatal ("temporary file name");
  1014.  
  1015.           tmpmade = 1;
  1016.  
  1017.           {
  1018.             FILE *tmp = ck_fopen (tmpname, "w+");
  1019.  
  1020.             if (cmd1 == 'l' || cmd1 == 'b')
  1021.               lf_copy (left, lenl, tmp);
  1022.             else
  1023.               lf_skip (left, lenl);
  1024.  
  1025.             if (cmd1 == 'r' || cmd1 == 'b')
  1026.               lf_copy (right, lenr, tmp);
  1027.             else
  1028.               lf_skip (right, lenr);
  1029.  
  1030.             ck_fflush (tmp);
  1031.  
  1032.             {
  1033.               pid_t pid;
  1034.               int wstatus;
  1035.  
  1036.               ignore_SIGINT = 1;
  1037.               checksigs ();
  1038.  
  1039. #ifdef _AMIGA
  1040.               ck_fclose (tmp);
  1041.                 {
  1042.                     char const *argv[3];
  1043.  
  1044.                     argv[0] = edbin;
  1045.                     argv[1] = tmpname;
  1046.                     argv[2] = 0;
  1047.  
  1048.                     wstatus = AMIGA_run_v(NULL, NULL, NULL, 0, NULL, 0, argv, NULL);
  1049.                 }
  1050.               tmp = ck_fopen (tmpname, "r+");
  1051. #else
  1052.               pid = vfork ();
  1053.               if (pid == 0)
  1054.                 {
  1055.                   char const *argv[3];
  1056.                   int i = 0;
  1057.  
  1058.                   argv[i++] = edbin;
  1059.                   argv[i++] = tmpname;
  1060.                   argv[i++] = 0;
  1061.  
  1062.                   execvp (edbin, (char **) argv);
  1063.                   write (STDERR_FILENO, edbin, strlen (edbin));
  1064.                   write (STDERR_FILENO, ": not found\n", 12);
  1065.                   _exit (1);
  1066.                 }
  1067.  
  1068.               if (pid < 0)
  1069.                 perror_fatal ("fork failed");
  1070.  
  1071.               while (waitpid (pid, &wstatus, 0) < 0)
  1072.                 if (errno == EINTR)
  1073.                   checksigs ();
  1074.                 else
  1075.                   perror_fatal ("wait failed");
  1076. #endif /* _AMIGA */
  1077.  
  1078.               ignore_SIGINT = 0;
  1079.  
  1080.               if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
  1081.                 fatal ("Subsidiary editor failed");
  1082.             }
  1083.  
  1084.             if (fseek (tmp, 0L, SEEK_SET) != 0)
  1085.               perror_fatal ("fseek");
  1086.             {
  1087.               /* SDIFF_BUFSIZE is too big for a local var
  1088.                  in some compilers, so we allocate it dynamically.  */
  1089.               char *buf = xmalloc (SDIFF_BUFSIZE);
  1090.               size_t size;
  1091.  
  1092.               while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  1093.                 {
  1094.                   checksigs ();
  1095.                   ck_fwrite (buf, size, outfile);
  1096.                 }
  1097.               ck_fclose (tmp);
  1098.  
  1099.               free (buf);
  1100.             }
  1101.             return 1;
  1102.           }
  1103.         default:
  1104.           give_help ();
  1105.           break;
  1106.         }
  1107.     }
  1108. }
  1109.  
  1110.  
  1111.  
  1112. /* Alternately reveal bursts of diff output and handle user commands.  */
  1113. static int
  1114. interact (diff, left, right, outfile)
  1115.      struct line_filter *diff;
  1116.      struct line_filter *left;
  1117.      struct line_filter *right;
  1118.      FILE *outfile;
  1119. {
  1120.   for (;;)
  1121.     {
  1122.       char diff_help[256];
  1123.       int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
  1124.  
  1125.       if (snarfed <= 0)
  1126.         return snarfed;
  1127.  
  1128.       checksigs ();
  1129.  
  1130.       switch (diff_help[0])
  1131.         {
  1132.         case ' ':
  1133.           puts (diff_help + 1);
  1134.           break;
  1135.         case 'i':
  1136.           {
  1137.             int lenl = atoi (diff_help + 1), lenr, lenmax;
  1138.             char *p = strchr (diff_help, ',');
  1139.  
  1140.             if (!p)
  1141.               fatal (diff_help);
  1142.             lenr = atoi (p + 1);
  1143.             lenmax = max (lenl, lenr);
  1144.  
  1145.             if (suppress_common_flag)
  1146.               lf_skip (diff, lenmax);
  1147.             else
  1148.               lf_copy (diff, lenmax, stdout);
  1149.  
  1150.             lf_copy (left, lenl, outfile);
  1151.             lf_skip (right, lenr);
  1152.             break;
  1153.           }
  1154.         case 'c':
  1155.           {
  1156.             int lenl = atoi (diff_help + 1), lenr;
  1157.             char *p = strchr (diff_help, ',');
  1158.  
  1159.             if (!p)
  1160.               fatal (diff_help);
  1161.             lenr = atoi (p + 1);
  1162.             lf_copy (diff, max (lenl, lenr), stdout);
  1163.             if (! edit (left, lenl, right, lenr, outfile))
  1164.               return 0;
  1165.             break;
  1166.           }
  1167.         default:
  1168.           fatal (diff_help);
  1169.           break;
  1170.         }
  1171.     }
  1172. }
  1173.  
  1174.  
  1175.  
  1176. /* temporary lossage: this is torn from gnu libc */
  1177. /* Return nonzero if DIR is an existing directory.  */
  1178. static int
  1179. diraccess (dir)
  1180.      char const *dir;
  1181. {
  1182.   struct stat buf;
  1183.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  1184. }
  1185.  
  1186. /* Return nonzero if FILE exists.  */
  1187. static int
  1188. exists (file)
  1189.      char const *file;
  1190. {
  1191.   struct stat buf;
  1192.   return stat (file, &buf) == 0;
  1193. }
  1194.  
  1195. /* These are the characters used in temporary filenames.  */
  1196. static char const letters[] =
  1197.   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1198.  
  1199. /* Generate a temporary filename.
  1200.    If DIR_SEARCH is nonzero, DIR and PFX are used as
  1201.    described for tempnam.  If not, a temporary filename
  1202.    in P_tmpdir with no special prefix is generated.  If LENPTR
  1203.    is not 0, *LENPTR is set the to length (including the
  1204.    terminating '\0') of the resultant filename, which is returned.
  1205.    This goes through a cyclic pattern of all possible filenames
  1206.    consisting of five decimal digits of the current pid and three
  1207.    of the characters in `letters'.  Data for tempnam and tmpnam
  1208.    is kept separate, but when tempnam is using P_tmpdir and no
  1209.    prefix (i.e, it is identical to tmpnam), the same data is used.
  1210.    Each potential filename is tested for an already-existing file of
  1211.    the same name, and no name of an existing file will be returned.
  1212.    When the cycle reaches its end (12345ZZZ), 0 is returned.  */
  1213.  
  1214.  
  1215. static char *
  1216. private_tempnam (dir, pfx, dir_search, lenptr)
  1217.      char const *dir;
  1218.      char const *pfx;
  1219.      int dir_search;
  1220.      size_t *lenptr;
  1221. {
  1222.   static char const tmpdir[] = PVT_tmpdir;
  1223.   static struct
  1224.     {
  1225.       char buf[3];
  1226.       char *s;
  1227.       size_t i;
  1228.     } infos[2], *info;
  1229.   static char *buf;
  1230.   static size_t bufsize = 1;
  1231.   static pid_t oldpid = 0;
  1232.   pid_t pid = getpid ();
  1233.   register size_t len, plen;
  1234.  
  1235.   if (dir_search)
  1236.     {
  1237.       register char const *d = getenv ("TMPDIR");
  1238.       if (d && !diraccess (d))
  1239.         d = 0;
  1240.       if (!d && dir && diraccess (dir))
  1241.         d = dir;
  1242.       if (!d && diraccess (tmpdir))
  1243.         d = tmpdir;
  1244. #ifndef _AMIGA
  1245. /* Might create havoc on the Amiga! So we take it out. */
  1246.       if (!d && diraccess ("/tmp"))
  1247.         d = "/tmp";
  1248. #endif
  1249.       if (!d)
  1250.         {
  1251.           errno = ENOENT;
  1252.           return 0;
  1253.         }
  1254.       dir = d;
  1255.     }
  1256.   else
  1257.     dir = tmpdir;
  1258.  
  1259.   if (pfx && *pfx)
  1260.     {
  1261.       plen = strlen (pfx);
  1262.       if (plen > 5)
  1263.         plen = 5;
  1264.     }
  1265.   else
  1266.     plen = 0;
  1267.  
  1268.   if (dir != tmpdir && !strcmp (dir, tmpdir))
  1269.     dir = tmpdir;
  1270.   info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0];
  1271.  
  1272.   if (pid != oldpid)
  1273.     {
  1274.       oldpid = pid;
  1275.       info->buf[0] = info->buf[1] = info->buf[2] = '0';
  1276.       info->s = &info->buf[0];
  1277.       info->i = 0;
  1278.     }
  1279.  
  1280.   len = strlen (dir) + 1 + plen + 8;
  1281.   if (bufsize <= len)
  1282.     {
  1283.       do
  1284.         {
  1285.           bufsize *= 2;
  1286.         }
  1287.       while (bufsize <= len);
  1288.  
  1289.       if (buf)
  1290.         free (buf);
  1291.       buf = xmalloc (bufsize);
  1292.     }
  1293.   for (;;)
  1294.     {
  1295. #ifdef _AMIGA
  1296.       int l = strlen(dir);
  1297.       char *dirsep = (l && dir[l - 1] != ':' && dir[l - 1] !='/') ? "/" : "";
  1298. #endif
  1299.       *info->s = letters[info->i];
  1300. #ifdef _AMIGA
  1301.       sprintf (buf, "%s%s%.*s%.5lu%.3s", dir, dirsep, (int) plen, pfx,
  1302.                (unsigned long) pid % 100000, info->buf);
  1303. #else
  1304.       sprintf (buf, "%s/%.*s%.5lu%.3s", dir, (int) plen, pfx,
  1305.                (unsigned long) pid % 100000, info->buf);
  1306. #endif
  1307.       if (!exists (buf))
  1308.         break;
  1309.       ++info->i;
  1310.       if (info->i > sizeof (letters) - 1)
  1311.         {
  1312.           info->i = 0;
  1313.           if (info->s == &info->buf[2])
  1314.             {
  1315.               errno = EEXIST;
  1316.               return 0;
  1317.             }
  1318.           ++info->s;
  1319.         }
  1320.     }
  1321.  
  1322.   if (lenptr)
  1323.     *lenptr = len;
  1324.   return buf;
  1325. }
  1326.